1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.error.handler;
12 import hip.console.log;
13 import hip.util.conv;
14 
15 /** 
16  * Base clas for documenting errors
17  */
18 
19 public class EngineErrorStack
20 {
21     public string stackName;
22     public string[] errorStack;
23 
24     private this(string stackName)
25     {
26         this.stackName = stackName;
27     }
28 
29     public static EngineErrorStack getNewStack(string stackName)
30     {
31         return new EngineErrorStack(stackName);
32     }
33     /** 
34      * Adds an error to the stack
35      * Params:
36      *   errorHeader = Error Short
37      *   errorMessage = Error Long
38      */
39     public void addError(string errorHeader, string errorMessage)
40     {
41         errorStack~= errorHeader ~ ": " ~ errorMessage;
42     }
43 
44     public void showStack()
45     {
46         rawerror("ErrorStack: " ~ stackName);
47         const int len = cast(int)this.errorStack.length;
48         for(int i = 0; i < len; i++)
49             rawerror("\t" ~ errorStack[i]);
50     }
51 }
52 
53 
54 import core.stdc.stdlib;
55 
56 version(iOS) 
57 {
58     extern(C) void terminateiOSApp(int code);
59     ///iOS has a special terminate function which ought to be called when something happens.
60     alias terminate = terminateiOSApp;
61 }
62 else
63 {
64     alias terminate = core.stdc.stdlib.exit;
65 }
66 
67 /** 
68  * Class Used for handling errors
69  */
70 public static class ErrorHandler
71 {
72     __gshared
73     {
74         private  bool HAS_ANY_ERROR_HAPPENNED = false;
75         private  EngineErrorStack currentStack;
76         private  EngineErrorStack[] stackHistory;
77         private  string[] warnHistory;
78         private  bool isListening = false;
79         public  string LAST_ERROR = "";
80     }
81     
82     /** 
83      * This function will look wether any error has happenned
84      *  stackName = This will help identify where the error ocurred
85      */
86     public static void startListeningForErrors(string stackName = "Default Error")
87     {
88         if(isListening)
89             stopListeningForErrors();
90         HAS_ANY_ERROR_HAPPENNED = false;
91         isListening = true;
92         currentStack = EngineErrorStack.getNewStack(stackName);
93     }
94     /** 
95      * Will stop listening and 
96      * Returns: HAS_ANY_ERROR_HAPPENNED
97      */
98     public static bool stopListeningForErrors()
99     {
100         if(HAS_ANY_ERROR_HAPPENNED)
101             stackHistory~= currentStack;
102         currentStack = null;
103         isListening = false;
104         return HAS_ANY_ERROR_HAPPENNED;
105     }
106 
107     private static void getError(lazy string errorHeader, lazy string error)
108     {
109         if(isListening)
110         {
111             LAST_ERROR = errorHeader ~ ": \n" ~ error;
112             currentStack.addError(errorHeader, error);
113         }
114     }
115 
116 
117     /** 
118      * This function adds to the error stack
119      * Params:
120      *   errorTitle = Error Header
121      *   errorMessage = Error Message
122      */
123     public static void showErrorMessage(string errorTitle, string errorMessage, bool isFatal = false)
124     {
125         import hip.util.string;
126         if(isFatal)
127         {
128             rawfatal(BigString(errorTitle, "\t[[", errorMessage, "]]").toString);
129         }
130         else
131         {
132             rawerror(BigString(errorTitle, "\t[[", errorMessage, "]]").toString);
133         }
134         getError(errorTitle, errorMessage);
135     }
136     public static void showWarningMessage(string warningTitle, string warningMessage)
137     {
138         rawwarn("\nWarning: " ~ warningTitle);
139         rawwarn(warningMessage);
140         warnHistory~= warningTitle~": "~warningMessage;
141     }
142 
143     /** 
144      * 
145      * Params:
146      *   expression = Expression for looking wether an error has happenned
147      *   errorTitle = Error Header
148      *   errorMessage = Error Message
149      * Returns: If the error happenned
150      */
151     public static bool assertErrorMessage(bool expression, string errorTitle, string errorMessage, bool isFatal = false,
152     string file = __FILE__, size_t line =__LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
153     {
154         import hip.util.string;
155         expression = !expression; //Negate the expression, as it must return wether error ocurred
156         if(expression)
157         {
158             String where;
159             version(HIPREME_DEBUG)
160             {
161                 where = String("at module '", mod, "' ", file, ":", line, "(", func, ")\n\t");
162             }
163 
164             showErrorMessage(String(where, errorTitle).toString, errorMessage, isFatal);
165         }
166         return expression;
167     }
168 
169     /** 
170      * 
171      * Params:
172      *   expression = Expression for looking wether an error has happenned
173      *   errorTitle = Error Header
174      *   errorMessage = Error Message
175      * Returns: If the error happenned
176      */
177     public static bool assertLazyErrorMessage(bool expression, lazy string errorTitle, lazy string errorMessage, bool isFatal = false,
178     string file = __FILE__, size_t line =__LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
179     {
180         expression = !expression; //Negate the expression, as it must return wether error ocurred
181         if(expression)
182         {
183             version(HIPREME_DEBUG)
184             {
185                 string where = "at module '"~mod~"' "~file~":"~to!string(line)~"("~func~")\n\t";
186             }
187             else{string where="";}
188             showErrorMessage(where~errorTitle, errorMessage, isFatal);
189         }
190         return expression;
191     }
192 
193     /**
194     *   If you're running on a loop or need to concat your failure message, prefer using assertLazyExit.
195     */
196     public static void assertExit(bool expression, string onAssertionFailure = "Assertion Failure",
197     string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
198     {
199         if(!expression)
200         {
201             cast(void)ErrorHandler.assertErrorMessage(false, "HipAssertion", onAssertionFailure, true,
202             file, line, mod, func);
203             terminate(EXIT_FAILURE);
204         }
205     }
206 
207     public static void assertLazyExit(bool expression, lazy string onAssertionFailure,
208     string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
209     {
210         if(!expression)
211         {
212             cast(void)ErrorHandler.assertLazyErrorMessage(false, "HipAssertion", onAssertionFailure, true,
213             file, line, mod, func);
214             terminate(EXIT_FAILURE);
215         }
216     }
217 
218     static immutable(string) assertReturn(string expression)(string onAssertionFailureMessage)
219     {
220         return `if(ErrorHandler.assertErrorMessage(`~expression~`, "HipAssertion", "`~onAssertionFailureMessage~
221         `"))return;`;
222     }
223 
224     public static void showEveryError()
225     {
226         foreach(stack; stackHistory)
227         {
228             stack.showStack();
229         }
230     }
231 }